#include <stdio.h>
#include <stdlib.h>
#include <math.h>

/* 
 * Calcula o erro atual
 * parametros:
 *  x: x na iteracao atual
 *  xAntes: x na iteracao passada
 *  n: tamanho dos vetores
 * retorno:
 *  erro na iteracao atual
 */
float calcularErro(float *x, float *xAntes, int n){
  float absDifAux, /* variavel auxiliar para o calculo da maior diferenca absoluta */
        absDifMaior = fabs(x[0] - xAntes[0]), /* maior diferenca absoluta */ 
        absXMaior = fabs(x[0]), /* maior x absoluto */
        absXAux; /* variavel auxiliar para o calculo do maior x absoluto */
  int i; /* contador */ 
  
  for(i = 1; i < n; ++i){
    absXAux = fabs(x[i]); /* calcula o x absoluto atual */
    absDifAux = fabs(x[i] - xAntes[i]); /* calcula a diferenca absoluta atual */
    
    /* verifica se o x absoluto atual e o maior ate o momento */
    if(absXAux > absXMaior){
      absXMaior = absXAux; /* armazena o maior x absoluto */
    }
    
    /* verifica se a diferenca absoluta atual e a maior ate o momento */
    if(absDifAux > absDifMaior){
      absDifMaior = absDifAux; /* armazena a maior diferenca absoluta */
    }
  }
  
  /* retorna o erro */
  return (absDifMaior / absXMaior);
}

int main(void){
  int ordemMat, /* ordem da matriz A */
      linhaAvaliada, /* linha do sistema a ser avaliada */
      maxIteracoes, /* numero maximo de iteracoes */
      i, /* contador */
      j, /* contador */
      k; /* contador */
  float erroPermitido, /* erro maximo permitido */
        **matA, /* matriz A */
        *vetB, /* vetor B */
        aux, /* variavel auxiliar */
        *x, /* vetor X */
        *xAntes, /* vetor X da iteracao anterior */
        *xAux, /* ponteiro auxiliar usado para trocar x e xAntes */
        erroAtual, /* erro da iteracao atual */
        *vetLinhaAvaliada, /* vetor com os indices da linha a ser avaliada */
        respLinhaAvaliada; /* resposta da linha a ser avaliada */
  
  /* le os parametros do teste */
  scanf("%d %d %f %d", &ordemMat, &linhaAvaliada, &erroPermitido, &maxIteracoes);
  
  /* aloca e le a matriz A */
  matA = (float **)malloc(sizeof(float *) * ordemMat); /* aloca o vetor dos indices com os ponteiros para cada linha da matriz */
  /* aloca e le cada linha */
  for(i = 0; i < ordemMat; ++i){
    matA[i] = (float *)malloc(sizeof(float) * ordemMat); /* aloca uma linha */
    /* le os elementos da linha */
    for(j = 0; j < ordemMat; ++j){
      scanf("%f", &matA[i][j]);
    }
  }

  /* aloca e le o vetor B */
  vetB = (float *)malloc(sizeof(float) * ordemMat); /* aloca o vetor */
  for(i = 0; i < ordemMat; ++i){ /* le os elementos */
    scanf("%f", &vetB[i]);
  }
  
  /* aloca o vetor que contera os indices da linha a ser avaliada */
  vetLinhaAvaliada = (float *)malloc(sizeof(float) * ordemMat);
  for(i = 0; i < ordemMat; ++i){ /* copia os elementos da linha a ser avaliada */
    vetLinhaAvaliada[i] = matA[linhaAvaliada][i];
  }
  respLinhaAvaliada = vetB[linhaAvaliada]; /* copia a resposta da linha a ser avaliada */
  
  x = (float *)malloc(sizeof(float) * ordemMat); /* aloca o vetor X */
  xAntes = (float *)malloc(sizeof(float) * ordemMat); /* aloca o vetor X da iteracao anterior */

  /* normaliza a matriz A e o vetor B */
  for(i = 0; i < ordemMat; ++i){
    aux = matA[i][i]; /* copia o valor do elemento da diagonal */
    
    for(j = 0; j < ordemMat; ++j){ /* divide os elementos da linha pelo elemento da diagonal */
      matA[i][j]  /= aux; 
    }
    
    vetB[i] /= aux; /* divide o elemento de B pelo elemento da diagonal */
    
    x[i] = vetB[i]; /* copia o elemento de B para X (para usar na primeira iteracao) */
    
    matA[i][i] = 0; /* zera o elemento da diagonal */
  }
  
  /* faz as iteracoes */
  k = 0; /* zera o contador de iteracoes */
  do{
    /* troca os vetores X e o X da iteracao anterior para realizar a iteracao atual */
    xAux = xAntes;
    xAntes = x;
    x = xAux;
    
    /* calcula o valor de X atual */
    for(i = 0; i < ordemMat; ++i){
      x[i] = 0; /* zera o elemento do vetor */
      for(j = 0; j < ordemMat; ++j){ /* computa X[i] = soma(A[i][j] * xAntes[j]) */
        x[i] += (matA[i][j] * xAntes[j]);
      }
      x[i] = vetB[i] - x[i]; /* computa X[i] = B[i] soma(A[i][j] * xAntes[j]) */
    }
    
    erroAtual = calcularErro(x, xAntes, ordemMat); /* calcula o erro da iteracao atual */
    
    ++k;
  //}while(k < maxIteracoes); /* verifica se o numero maximo de iteracoes foi alcancado */
  }while(k < maxIteracoes && erroAtual > erroPermitido);  /* verifica se o numero maximo de iteracoes ou o erro maximo foram alcancados */
  
  printf("Iterations: %d\n", k); /* imprime o numero de iteracoes */
  
  /* calcula o valor da linha a ser avaliada */
  aux = 0; /* zera a variavel auxiliar que ira computar a soma */
  for(i = 0; i < ordemMat; ++i){ /* computa a soma da linha */
    aux += x[i] * vetLinhaAvaliada[i];
  }
  printf("RowTest: %d => [%f] =? %f\n", linhaAvaliada, aux, respLinhaAvaliada);  /* imprime o rreultado da soma da linha e o valor esperado */
  
  /* desaloca a matriz A */
  for(i = 0; i < ordemMat; ++i){
    free(matA[i]); /* desaloca a linha */
  }
  free(matA); /* desaloca o vetor com os ponteiros para as linhas */
  
  free(vetB); /* desaloca o vetor B*/
  free(x); /* desaloca o vetor X */
  free(xAntes); /* desaloca o vetor X da iteracao anterior */

  free(vetLinhaAvaliada); /* desaloca o vetor da linha a ser avaliada */

  return 0;
}
